use axum::{response::{Html, IntoResponse}, Extension};
use axum::http::{HeaderMap, Request, StatusCode};
use async_graphql::http::{playground_source, GraphQLPlaygroundConfig, GraphiQLSource};
use async_graphql::{Context, EmptySubscription, Error, Guard, Object, Schema, Result, ErrorExtensions};
use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
use axum::body::Body;
use futures_util::task::waker;
use meilisearch_sdk::client::Client;
use minio_rsc::Minio;
use redis::aio::MultiplexedConnection;
use sea_orm::DatabaseConnection;
use tracing::{debug, error, warn};
use crate::graphql::graphiql::graphiql_source;
use crate::graphql::product_ad_graphql::{ProductADMutation, ProductADQuery};
use crate::graphql::product_graphql::{ProductMutation, ProductQuery};
use crate::graphql::user_graphql::{UserMutation, UserQuery};
use crate::utility::jwt::{AccessClaims, JwtUtil, UserInfo};

pub mod product_ad_graphql;
pub mod product_graphql;
pub mod user_graphql;
pub mod system_graphql;
pub mod order_graphql;
mod file_graphql;
mod graphiql;

#[derive(Default)]
pub struct MutationRoot;

#[Object]
impl MutationRoot {
    async fn product_ad(&self) -> ProductADMutation {
        ProductADMutation::default()
    }

    async fn product(&self) -> ProductMutation {
        ProductMutation::default()
    }

    async fn user(&self) -> UserMutation {
        UserMutation::default()
    }

    async fn sys_auth(&self) -> system_graphql::SysAuthMutation {
        system_graphql::SysAuthMutation::default()
    }

    async fn order(&self) -> order_graphql::OrderMutation {
        order_graphql::OrderMutation::default()
    }
}

#[derive(Default)]
pub struct QueryRoot;

#[Object]
impl QueryRoot {
    async fn product(&self) -> ProductQuery {
        ProductQuery::default()
    }
    async fn product_ad(&self) -> ProductADQuery {
        ProductADQuery::default()
    }

    async fn user(&self) -> UserQuery {
        UserQuery::default()
    }

    async fn sys_auth(&self) -> system_graphql::SysAuthQuery {
        system_graphql::SysAuthQuery::default()
    }

    async fn file(&self) -> file_graphql::FileQuery {
        file_graphql::FileQuery::default()
    }

    async fn order(&self) -> order_graphql::OrderQuery {
        order_graphql::OrderQuery::default()
    }
}

pub type AppSchema = Schema<QueryRoot, MutationRoot, EmptySubscription>;
pub fn create_schema(db: DatabaseConnection, redis: MultiplexedConnection, minio: Minio, meilisearch_client: Client) -> AppSchema {
    Schema::build(QueryRoot::default(),
                  MutationRoot::default(),
                  EmptySubscription)
        .data(db)
        .data(redis)
        .data(minio)
        .data(meilisearch_client)
        // .disable_introspection()
        .finish()
}


pub async fn graphql_handler(
    schema: Extension<AppSchema>,
    user_info: Option<Extension<UserInfo>>,
    req: GraphQLRequest,
) -> GraphQLResponse {
    let mut gql_request = req.into_inner();

    // error!("请求参数user_info: {:?}", user_info.0);
    // 检查是否存在用户信息
    if let Some(Extension(user)) = user_info {
        debug!("请求用户: {:?}", user);
        gql_request = gql_request.data(user);
    }

    schema.execute(gql_request).await.into()

}

pub async fn graphql_playground() -> impl IntoResponse {
    // Html(playground_source(
    //     GraphQLPlaygroundConfig::new("/").subscription_endpoint("/"),
    // )
    // GraphiQLSource::build().subscription_endpoint("/").endpoint("/").finish())
    Html(graphiql_source("/api/graphql"))

    // Html(AltairSource::build().finish())
}


// 2. 权限校验Guard
pub struct AuthGuard<'a>(&'a str);

impl Guard for  AuthGuard<'_> {
    async fn check(&self, ctx: &Context<'_>) -> Result<()> {
        // 获取当前用户
        let user = ctx.data_opt::<UserInfo>()
            .ok_or_else(|| {
                let mut err = Error::new("用户未登录，请先登录");
                err.extend_with(|_, e| e.set("code", "UNAUTHORIZED"))
            })?;

        if self.0 == "" || user.authorities.contains("root") {
            return Ok(())
        }

        // 检查权限
        if user.authorities.contains(self.0) {
            Ok(())
        } else {
            let err = Error::new("权限不足")
                .extend_with(|_, e| e.set("code", "FORBIDDEN"));
            Err(err)
        }
    }
}